﻿using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.Filters;
using Swashbuckle.AspNetCore.SwaggerGen;
using Swashbuckle.AspNetCore.SwaggerUI;
using ZeroSum.Extend.Swagger.Configurations;
using ZeroSum.Extend.Swagger.Constants;
using ZeroSum.Extend.Swagger.Filters;
using SecurityRequirementsOperationFilter = Swashbuckle.AspNetCore.Filters.SecurityRequirementsOperationFilter;

namespace ZeroSum.Extend.Swagger;

public static class SwaggerExtension
{
    /// <summary>
    ///     注册Swagger
    /// </summary>
    /// <param name="services"></param>
    /// <param name="configuration"></param>
    /// <param name="action"></param>
    /// <returns></returns>
    public static IServiceCollection AddSwaggerDoc(this IServiceCollection services, IConfiguration configuration,
        Action<SwaggerGenOptions>? action = default)
    {
        var options = configuration.GetSection(SwaggerConst.SwaggerOptions).Get<SwaggerOptions>();
        if (options?.Enabled != true)
            return services;

        services.AddSwaggerGen(c =>
        {
            c.SwaggerDoc($"v{options.Version}",
                new OpenApiInfo {Title = options.Title, Version = $"{options.Version}"});

            c.SupportNonNullableReferenceTypes();

            c.UseAllOfToExtendReferenceSchemas();

            c.OperationFilter<CamelCaseNamingOperationFilter>();

            // 开启加权小锁
            c.OperationFilter<AddResponseHeadersFilter>();
            c.OperationFilter<AppendAuthorizeToSummaryOperationFilter>();

            //// 在header中添加token，传递到后台
            c.OperationFilter<SecurityRequirementsOperationFilter>();

            //权限Token
            c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
            {
                Description = "请输入带有Bearer的Token，形如 “Bearer {Token}” ",
                Name = "Authorization",
                In = ParameterLocation.Header,
                Type = SecuritySchemeType.ApiKey
            });

            c.AddSecurityRequirement(new OpenApiSecurityRequirement
            {
                {
                    new OpenApiSecurityScheme
                    {
                        Reference = new OpenApiReference {Type = ReferenceType.SecurityScheme, Id = "oauth2"}
                    },
                    new[] {"readAccess", "writeAccess"}
                }
            });
            if (options.EnabledDocumentationFile)
            {
                var basePath =
                    Path.GetDirectoryName(typeof(SwaggerExtension).Assembly
                        .Location)!; //获取应用程序所在目录（绝对，不受工作目录影响，建议采用此方法获取路径）
                var xmlPath = Path.Combine(basePath, options.DocumentationFilePath!);
                if (File.Exists(xmlPath))
                    c.IncludeXmlComments(xmlPath);
            }

            action?.Invoke(c);
        });
        return services;
    }

    public static IApplicationBuilder UseSwaggerDoc(this IApplicationBuilder app,
        Action<SwaggerUIOptions>? action = default)
    {
        var provider = app.ApplicationServices;
        var configuration = provider.GetService<IConfiguration>();

        var options = configuration?.GetSection(SwaggerConst.SwaggerOptions).Get<SwaggerOptions>();
        if (options?.Enabled != true)
            return app;

        app.UseSwagger();
        app.UseSwaggerUI(c =>
        {
            c.SwaggerEndpoint(options.Url, $"{options.Title} V{options.Version}");
            c.DocumentTitle = options.Title;
            AddDefaultInterceptor(c);

            //开启MiniProfiler性能监控
            if (options.MiniProfiler)
                c.IndexStream = () => typeof(SwaggerExtension).Assembly.GetManifestResourceStream(
                    $"{typeof(SwaggerExtension).Assembly.GetName().Name}.Htmls.index-mini-profiler.html");
            else
                c.IndexStream = () => typeof(SwaggerExtension).Assembly.GetManifestResourceStream(
                    $"{typeof(SwaggerExtension).Assembly.GetName().Name}.Htmls.Index.html");

            action?.Invoke(c);
        });

        return app;
    }

    /// <summary>
    ///     添加默认请求/响应拦截器
    /// </summary>
    /// <param name="swaggerUiOptions"></param>
    private static void AddDefaultInterceptor(SwaggerUIOptions swaggerUiOptions)
    {
        // 配置自动登录token
        swaggerUiOptions.UseRequestInterceptor("(request) => { return defaultRequestInterceptor(request); }");
        swaggerUiOptions.UseResponseInterceptor("(response) => { return defaultResponseInterceptor(response); }");
    }
}